#pragma once

#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <queue>
#include <sstream>
#include <thread>
#include <vector>

#include "list.h"
#include "semaphore.h"
#include "thread_pool.h"
#include "util.h"

class LogContext;
class LogChannel;
class LogWriter;
class Logger;

using LogContextPtr = ::std::shared_ptr<LogContext>;

typedef enum { LTrace = 0, LDebug, LInfo, LWarn, LError } LogLevel;

Logger& getLogger();
void setLogger(Logger* logger);

/**
 * 日志类
 */
class Logger : public ::std::enable_shared_from_this<Logger>,
               public noncopyable {
public:
    friend class AsyncLogWriter;
    using Ptr = ::std::shared_ptr<Logger>;

    /**
     * 获取日志单例
     * @return
     */
    static Logger& Instance();

    explicit Logger(const ::std::string& loggerName);
    ~Logger();

    /**
     * 添加日志通道，非线程安全的
     * @param channel log通道
     */
    void add(const ::std::shared_ptr<LogChannel>& channel);

    /**
     * 删除日志通道，非线程安全的
     * @param name log通道名
     */
    void del(const ::std::string& name);

    /**
     * 获取日志通道，非线程安全的
     * @param name log通道名
     * @return 线程通道
     */
    ::std::shared_ptr<LogChannel> get(const ::std::string& name);

    /**
     * 设置写log器，非线程安全的
     * @param writer 写log器
     */
    void setWriter(const ::std::shared_ptr<LogWriter>& writer);

    /**
     * 设置所有日志通道的log等级
     * @param level log等级
     */
    void setLevel(LogLevel level);

    /**
     * 获取logger名
     * @return logger名
     */
    const ::std::string& getName() const;

    /**
     * 写日志
     * @param ctx 日志信息
     */
    void write(const LogContextPtr& ctx);

private:
    /**
     * 写日志到各channel，仅供AsyncLogWriter调用
     * @param ctx 日志信息
     */
    void writeChannels(const LogContextPtr& ctx);
    void writeChannels_l(const LogContextPtr& ctx);

private:
    LogContextPtr _last_log;
    ::std::string _logger_name;
    ::std::shared_ptr<LogWriter> _writer;
    ::std::map<::std::string, ::std::shared_ptr<LogChannel>> _channels;

    std::atomic_flag _busy{false};
};

///////////////////LogContext///////////////////
/**
 * 日志上下文
 */
class LogContext : public ::std::ostringstream {
public:
    //_file,_function改成string保存，目的是有些情况下，指针可能会失效
    // 比如说动态库中打印了一条日志，然后动态库卸载了，那么指向静态数据区的指针就会失效
    LogContext() = default;
    LogContext(LogLevel level,
               const char* file,
               const char* function,
               int line,
               const char* module_name,
               const char* flag);
    ~LogContext() = default;

    LogLevel _level;
    int _line;
    int _repeat = 0;
    ::std::string _file;
    ::std::string _function;
    ::std::string _thread_name;
    ::std::string _module_name;
    ::std::string _flag;
    struct timeval _tv;

    const ::std::string& str();

private:
    bool _got_content = false;
    ::std::string _content;
};

/**
 * 日志上下文捕获器
 */
class LogContextCapture {
public:
    using Ptr = ::std::shared_ptr<LogContextCapture>;

    LogContextCapture(Logger& logger,
                      LogLevel level,
                      const char* file,
                      const char* function,
                      int line,
                      const char* flag = "");
    LogContextCapture(const LogContextCapture& that);
    ~LogContextCapture();

    /**
     * 输入std::endl(回车符)立即输出日志
     * @param f ::std::endl(回车符)
     * @return 自身引用
     */
    LogContextCapture& operator<<(::std::ostream& (*f)(::std::ostream&));

    template <typename T>
    LogContextCapture& operator<<(T&& data) {
        if (!_ctx) {
            return *this;
        }
        (*_ctx) << std::forward<T>(data);
        return *this;
    }

    void clear();

private:
    LogContextPtr _ctx;
    Logger& _logger;
};

///////////////////LogWriter///////////////////
/**
 * 写日志器
 */
class LogWriter : public noncopyable {
public:
    LogWriter() = default;
    virtual ~LogWriter() = default;

    virtual void write(const LogContextPtr& ctx, Logger& logger) = 0;
};

class AsyncLogWriter : public LogWriter {
public:
    AsyncLogWriter();
    ~AsyncLogWriter();

private:
    void run();
    void flushAll();
    void write(const LogContextPtr& ctx, Logger& logger) override;

private:
    bool _exit_flag;
    semaphore _sem;
    std::mutex _mutex;
    std::shared_ptr<std::thread> _thread;
    List<std::pair<LogContextPtr, Logger*>> _pending;
};

///////////////////LogChannel///////////////////
/**
 * 日志通道
 */
class LogChannel : public noncopyable {
public:
    LogChannel(const std::string& name, LogLevel level = LTrace);
    virtual ~LogChannel();

    virtual void write(const Logger& logger, const LogContextPtr& ctx) = 0;
    const std::string& name() const;
    void setLevel(LogLevel level);
    static std::string printTime(const timeval& tv);

protected:
    /**
     * 打印日志至输出流
     * @param ost 输出流
     * @param enable_color 是否启用颜色
     * @param enable_detail 是否打印细节(函数名、源码文件名、源码行)
     */
    virtual void format(const Logger& logger,
                        std::ostream& ost,
                        const LogContextPtr& ctx,
                        bool enable_color = true,
                        bool enable_detail = true);

protected:
    std::string _name;
    LogLevel _level;
};

/**
 * 输出日志至终端，支持输出日志至android logcat
 */
class ConsoleChannel : public LogChannel {
public:
    ConsoleChannel(const std::string& name = "ConsoleChannel",
                   LogLevel level = LTrace);
    ~ConsoleChannel() override = default;

    void write(const Logger& logger, const LogContextPtr& logContext) override;
};

/**
 * 输出日志至文件
 */
class FileChannelBase : public LogChannel {
public:
    FileChannelBase(const std::string& name = "FileChannelBase",
                    const std::string& path = exePath() + ".log",
                    LogLevel level = LTrace);
    ~FileChannelBase() override;

    void write(const Logger& logger, const LogContextPtr& ctx) override;
    bool setPath(const std::string& path);
    const std::string& path() const;

protected:
    virtual bool open();
    virtual void close();
    virtual size_t size();

protected:
    std::string _path;
    std::ofstream _fstream;
};

class Ticker;

/**
 * 自动清理的日志文件通道
 * 默认最多保存30天的日志
 */
class FileChannel : public FileChannelBase {
public:
    FileChannel(const std::string& name = "FileChannel",
                const std::string& dir = exeDir() + "log/",
                LogLevel level = LTrace);
    ~FileChannel() override = default;

    /**
     * 写日志时才会触发新建日志文件或者删除老的日志文件
     * @param logger
     * @param stream
     */
    void write(const Logger& logger, const LogContextPtr& ctx) override;

    /**
     * 设置日志最大保存天数
     * @param max_day 天数
     */
    void setMaxDay(size_t max_day);

    /**
     * 设置日志切片文件最大大小
     * @param max_size 单位MB
     */
    void setFileMaxSize(size_t max_size);

    /**
     * 设置日志切片文件最大个数
     * @param max_count 个数
     */
    void setFileMaxCount(size_t max_count);

private:
    /**
     * 删除日志切片文件，条件为超过最大保存天数与最大切片个数
     */
    void clean();

    /**
     * 检查当前日志切片文件大小，如果超过限制，则创建新的日志切片文件
     */
    void checkSize(time_t second);

    /**
     * 创建并切换到下一个日志切片文件
     */
    void changeFile(time_t second);

    /**
     * @brief 压缩文件
     *
     * @param file
     */
    std::string compress(const std::string& file);

private:
    bool _can_write = false;
    // 默认最多保存30天的日志文件
    size_t _log_max_day = 30;
    // 每个日志切片文件最大默认128MB
    size_t _log_max_size = 128;
    // 最多默认保持30个日志切片文件
    size_t _log_max_count = 30;
    // 当前日志切片文件索引
    int64_t _last_day = -1;
    time_t _last_check_time = 0;
    std::string _dir;
    std::string _name;
    std::deque<std::string> _log_file_queue;
    ThreadPool _tp{1, "FCBackend"};
};

class BaseLogFlagInterface {
protected:
    virtual ~BaseLogFlagInterface() {}
    // 获得日志标记Flag
    const char* getLogFlag() { return _log_flag; }
    void setLogFlag(const char* flag) { _log_flag = flag; }

private:
    const char* _log_flag = "";
};

class LoggerWrapper {
public:
    template <typename First, typename... ARGS>
    static inline void printLogArray(Logger& logger,
                                     LogLevel level,
                                     const char* file,
                                     const char* function,
                                     int line,
                                     First&& first,
                                     ARGS&&... args) {
        LogContextCapture log(logger, level, file, function, line);
        log << std::forward<First>(first);
        appendLog(log, std::forward<ARGS>(args)...);
    }

    static inline void printLogArray(Logger& logger,
                                     LogLevel level,
                                     const char* file,
                                     const char* function,
                                     int line) {
        LogContextCapture log(logger, level, file, function, line);
    }

    template <typename Log, typename First, typename... ARGS>
    static inline void appendLog(Log& out, First&& first, ARGS&&... args) {
        out << std::forward<First>(first);
        appendLog(out, std::forward<ARGS>(args)...);
    }

    template <typename Log>
    static inline void appendLog(Log& out) {
        DG_UNUSED(out);
    }

    // printf样式的日志打印
    static void printLog(Logger& logger,
                         int level,
                         const char* file,
                         const char* function,
                         int line,
                         const char* fmt,
                         ...);
    static void printLogV(Logger& logger,
                          int level,
                          const char* file,
                          const char* function,
                          int line,
                          const char* fmt,
                          va_list ap);
};

// 可重置默认值
extern Logger* g_defaultLogger;

// 用法: DebugL << 1 << "+" << 2 << '=' << 3;
#define WriteL(level) \
    LogContextCapture(getLogger(), level, __FILE__, __FUNCTION__, __LINE__)
#define TraceL WriteL(LTrace)
#define DebugL WriteL(LDebug)
#define InfoL WriteL(LInfo)
#define WarnL WriteL(LWarn)
#define ErrorL WriteL(LError)

// 只能在虚继承BaseLogFlagInterface的类中使用
#define WriteF(level)  \
    LogContextCapture( \
        getLogger(), level, __FILE__, __FUNCTION__, __LINE__, getLogFlag())
#define TraceF WriteF(LTrace)
#define DebugF WriteF(LDebug)
#define InfoF WriteF(LInfo)
#define WarnF WriteF(LWarn)
#define ErrorF WriteF(LError)

// 用法: LogD("%d + %s = %c", 1 "2", 'c');
#define PrintLog(level, ...) \
    LoggerWrapper::printLog( \
        getLogger(), level, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define PrintT(...) PrintLog(LTrace, ##__VA_ARGS__)
#define PrintD(...) PrintLog(LDebug, ##__VA_ARGS__)
#define PrintI(...) PrintLog(LInfo, ##__VA_ARGS__)
#define PrintW(...) PrintLog(LWarn, ##__VA_ARGS__)
#define PrintE(...) PrintLog(LError, ##__VA_ARGS__)

// 用法: LogD(1, "+", "2", '=', 3);
// 用于模板实例化的原因，如果每次打印参数个数和类型不一致，可能会导致二进制代码膨胀
#define LogL(level, ...)                          \
    LoggerWrapper::printLogArray(getLogger(),     \
                                 (LogLevel)level, \
                                 __FILE__,        \
                                 __FUNCTION__,    \
                                 __LINE__,        \
                                 ##__VA_ARGS__)
#define LogT(...) LogL(LTrace, ##__VA_ARGS__)
#define LogD(...) LogL(LDebug, ##__VA_ARGS__)
#define LogI(...) LogL(LInfo, ##__VA_ARGS__)
#define LogW(...) LogL(LWarn, ##__VA_ARGS__)
#define LogE(...) LogL(LError, ##__VA_ARGS__)